package com.fise.mwtvc.socket

import android.util.Log
import com.fise.mwtvc.AndroidUtils
import com.fise.mwtvc.DeviceUtils
import com.fise.mwtvc.socket.bean.WsKeyResponse
import com.fise.mwtvc.socket.bean.WsLogin
import com.fise.mwtvc.socket.bean.WsResponse
import com.google.gson.GsonBuilder
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.consumeAsFlow
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.selects.select
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.Response
import okhttp3.WebSocket
import okhttp3.WebSocketListener
import okio.ByteString

class WebSocketClient {
    private val DEBUG = true;
    private val TAG = "WebSocketClient"

    //    private var SERVER_URL = getUrl()
    private val SEND_TIMEOUT = 10 * 1000L
    private val HEATBEAT_TIMEOUT = 60 * 1000L
    private val RECONNECT_TIME_WAIT = 5000L
    private val RECONNECT_TIME_STEP = 5000L
    private val RECONNECT_TIME_MAX = 60 * 1000L

    private val client = OkHttpClient()
    private val request
        get() = Request.Builder().url(getUrl()).build()
    private val listener
        get() = WsNotificationListener(scope)
    private val gson by lazy {
        GsonBuilder().registerTypeAdapter(WsResponse::class.java, WsResponseDeserializer())
            .create()
    }

    //用于检测心跳包重试次数。
    private var heartBeatError: Int = 0;

    private var scope: CoroutineScope = CoroutineScope(Dispatchers.IO)
    private var isOpen = true
    private var curReconnectTime = RECONNECT_TIME_WAIT
    private var ws: WebSocket? = null
    private var jobHeatBeat: Job? = null
    private var channel: Channel<Unit>? = null
    private var rspChannel: Channel<WsKeyResponse>? = null
    private var retryCount: Int = 0;

    val deviceParam
        get() = run {
            val map = HashMap<String, String>();
            map["deviceSn"] = DeviceUtils.getSn()
            map["type"] = "2"//心跳包
            gson.toJson(map)
        }

    val uploadParam
        get() = run {
            val map = HashMap<String, String>();
            map["deviceSn"] = DeviceUtils.getSn()
//            map["type"] = "1"//更新数据
//            map["verType"] = FaceHelper.SDK_TYPE.toString()
//            map["cpuId"] = DeviceUtils.getCPUSerial()
//            map["currentVer"] =
//                DeviceUtils.getVersionCode(ContextProvider.get().context).toString()
            gson.toJson(map)
        }

    fun start(): Flow<WsKeyResponse> {
        close()
        isOpen = true
        curReconnectTime = RECONNECT_TIME_WAIT
        scope = CoroutineScope(Dispatchers.IO)
        try {
            createWebSocket()
        } catch (e: Exception) {
            e.printStackTrace()
        }
        val c = Channel<WsKeyResponse>(Channel.UNLIMITED)
        rspChannel = c
        return c.consumeAsFlow()
    }

    fun close() {
        isOpen = false
        scope.cancel()
        jobHeatBeat?.cancel()
        jobHeatBeat = null
        channel?.close()
        channel = null
        rspChannel?.close()
        rspChannel = null
        try {
            ws?.close(4000, "close")
        } catch (e: Exception) {
            e.printStackTrace()
        }
        ws = null
    }

    private suspend fun reconnect() {
        ws = null
        jobHeatBeat?.cancel()
        jobHeatBeat = null
        if (!isOpen) return
        logi("准备重连", curReconnectTime / 1000, "s")
        delay(curReconnectTime)
        curReconnectTime += RECONNECT_TIME_STEP
        if (curReconnectTime > RECONNECT_TIME_MAX) {
            curReconnectTime = RECONNECT_TIME_WAIT
        }
        logi("开始重连")
        retryCount++;
        try {
            createWebSocket()
        } catch (e: Exception) {
            e.printStackTrace()
        }
    }

    private fun createWebSocket() {
        ws = null
        ws = client.newWebSocket(request, listener)
    }

    private fun getUrl(): String {
        val sn = DeviceUtils.getSn()
        Log.d(TAG, " sn:${sn}")
        //return "wss://szmaowo.com:8087/jeecg-boot/equiSocket/${sn}"
        return "wss://szmaowo.com:8087/jeecg-boot/cloudSocket/${sn}"
    }


    private inner class WsNotificationListener(
        private val scope: CoroutineScope
    ) : WebSocketListener() {
        override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
            super.onClosed(webSocket, code, reason)
            logi("onClosed: ", webSocket, code, reason)
            webSocket.cancel()
            scope.launch {
                reconnect()
            }
        }

        override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
            super.onClosing(webSocket, code, reason)
            logi("onClosing: ", webSocket, code, reason)
        }

        override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
            super.onFailure(webSocket, t, response)
            logi("onFailure: ", webSocket, t, response)
            webSocket.cancel()
            scope.launch {
                reconnect()
            }
        }

        override fun onMessage(webSocket: WebSocket, text: String) {
            super.onMessage(webSocket, text)
            logi("onMessage: ", webSocket, text)
            scope.launch {
                if (channel?.isClosedForSend == false) {
                    try {
                        channel?.send(Unit)
                    } catch (e: Exception) {
                        e.printStackTrace()
                    }
                    channel = null
                }
                if (text.isEmpty()) return@launch
                // if (!text.startsWith("{")) return@launch
                try {
                    Log.d(TAG, "text:${text}")

                    /*
                        private fun sendCustom() {
        sendBroadcast(Intent("com.fise.menu.show"))
    }

    private fun shutdown() {
        sendBroadcast(Intent("com.fise.system.shutdown"))
    }
                     */

                    when (text) {
                        "xiping" -> {

                        }

                        "jinyin" -> {

                        }

                        "home" -> {

                        }

                        "caidan" -> {

                        }

                        "shang" -> {

                        }

                        "xia" -> {

                        }

                        "zuo" -> {

                        }

                        "you" -> {

                        }

                        "ok" -> {

                        }

                        "fanhui" -> {

                        }

                        "shezhi" -> {

                        }

                        "jia" -> {

                        }

                        "jian" -> {

                        }

                        "guanji" -> {
                            // sendBroadcast(Intent("com.fise.system.shutdown"))

                            //ContextProvider.get().context.sendBroadcast(Intent("com.fise.system.shutdown"))
                            AndroidUtils.shutdown()
                        }

                        "chongqi" -> {
                            // sendBroadcast(Intent("com.fise.system.shutdown"))

                            //ContextProvider.get().context.sendBroadcast(Intent("com.fise.system.shutdown"))
                           // AndroidUtils.shutdown(false, "", false)
                            AndroidUtils.rebootSafeMode()

                        }
                    }


                    /*  if(!"heartbeat".equals(text)){
                          val rsp = gson.fromJson(text, WsKeyResponse::class.java) ?: return@launch
  //                    logi(rsp.toString())
  //                    webSocket.send(gson.toJson(WsOk()))
                          rspChannel?.send(rsp)
                          heartBeatError = 0;
                      }*/
                } catch (e: Exception) {
                    e.printStackTrace()
                }
            }
        }

        override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
            super.onMessage(webSocket, bytes)
            logi("onMessage: ", webSocket, bytes)
        }

        override fun onOpen(webSocket: WebSocket, response: Response) {
            super.onOpen(webSocket, response)
            logi("onOpen: ", webSocket, response)
            scope.launch {
                curReconnectTime = RECONNECT_TIME_WAIT
                jobHeatBeat = scope.launch {
                    while (isActive && isOpen) {
                        delay(HEATBEAT_TIMEOUT)
                        sendHeatBeat()
                    }
                }

                try {
                    val data = WsLogin()
                    val json = gson.toJson(data)
                    logi(json)
                    webSocket.send(json)
                    if (retryCount > 5) {
                        getUserInfo(webSocket);
                    }
                    retryCount = 0;
                    heartBeatError = 0
                } catch (e: Exception) {
                    e.printStackTrace()
                }
//                uploadCurrentDevice();
            }
        }

        private suspend fun getUserInfo(webSocket: WebSocket) {
            //模拟下发用户更新
            val fakeMsg = WsResponse(type = WsResponse.TYPE_USER, code = 0, msg = "fake msg");
            onMessage(webSocket, gson.toJson(fakeMsg))
        }

        //上传当前数据
        private suspend fun uploadCurrentDevice() {
            try {
                ws?.send(uploadParam)
                logi("发送当前设备的数据$uploadParam")
            } catch (e: Exception) {
                e.printStackTrace()
            }
        }

        private suspend fun sendHeatBeat() {
            try {
                heartBeatError++;
                ws?.send(deviceParam)
                logi("发送心跳$deviceParam")
            } catch (e: Exception) {
                e.printStackTrace()
            }
            val job = scope.launch {
                delay(SEND_TIMEOUT)
                if (DEBUG) {
                    try {
                        channel?.send(Unit)
                        delay(10)
                    } catch (e: Exception) {
                    }
                }
            }
            val c = Channel<Unit>()
            channel = c
            val rec: Boolean = try {
                select {
                    job.onJoin { false }
                    c.onReceive { true }
                }
            } catch (e: Exception) {
                e.printStackTrace()
                false
            }
            try {
                c.close()
                channel = null
                if (!rec) {
                    logi("心跳超时")
                    ws?.close(4000, "timeout")
                }
            } catch (e: Exception) {
                e.printStackTrace()
            }
            logi("heartBeatError:${heartBeatError}")
            //如果心跳不回复超过两次，则认为已经处于假死状态。
            if (heartBeatError > 2) {
                ws?.close(4000, "timeout")
            }
        }
    }

    private fun logi(vararg any: Any?) {
        if (!DEBUG) return
        val log = any.joinToString { it.toString() }
        Log.i(WebSocketClient::class.java.simpleName, log)
    }
}